﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Threading;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace SnakeX
{
	public partial class frmMain : Form
	{

		#region Init Methods


		/// <summary>
		/// Constructor.
		/// Initializing game.
		/// </summary>
		public frmMain()
		{
			// Initializing Form.
			InitForm();

			// Initializing Direct3D device object.
			InitD3DDevice();

			// Initializing square object.
			square = new SnakeXSquare(device.D3dDevice);

			// Initializing text objects.
			InitText();

			// Setting paint event.
			this.Paint += new PaintEventHandler(this.frmMain_Paint);

			// Initializing keyboard device.
			keyboard = new SnakeXKeyboard(this);

			// Preparing menu.
			InitMenu();
		}


		/// <summary>
		/// Initializing form.
		/// </summary>
		private void InitForm()
		{
			this.SuspendLayout();
			this.ClientSize = new Size(800, 600);
			this.StartPosition = FormStartPosition.CenterScreen;
			this.MaximizeBox = false;
			this.FormBorderStyle = FormBorderStyle.FixedSingle;
			this.Text = "SnakeX";
			this.SetStyle(ControlStyles.AllPaintingInWmPaint
				| ControlStyles.Opaque, true);
			this.ResumeLayout(false);
		}


		/// <summary>
		/// Initializing Direct3D device object.
		/// </summary>
		private void InitD3DDevice()
		{
			device = new SnakeXDevice(this, true);
			device.D3dDevice.Disposing += new EventHandler(D3dDevice_Disposing);
		}


		/// <summary>
		/// Initializing text objects.
		/// </summary>
		private void InitText()
		{
			// Title text.
			titleText = new SnakeXText(device.D3dDevice, new Size(64, 64),
				FontWeight.Bold, "Tahoma");

			// Normal text.
			normalText = new SnakeXText(device.D3dDevice, new Size(16, 16),
				FontWeight.Normal, "Tahoma");
		}


		/// <summary>
		/// Initializing menu.
		/// </summary>
		private void InitMenu()
		{
			// Loading menu BG texture.
			menuTexture = TextureLoader.FromFile(device.D3dDevice,
				Properties.Settings.Default.ImagePath,
				800, 300, 0, 0, Format.Unknown, Pool.Managed, Filter.Linear,
				Filter.Linear, Color.FromArgb(0, 0, 255).ToArgb());

			// Initializing menu sprite.
			menuSprite = new Sprite(device.D3dDevice);

			// Setting draw menu parameter.
			drawMenu = true;
		}

		/// <summary>
		/// Initializing game.
		/// </summary>
		private void InitGame()
		{
			// Initializing snake object.
			snake = new SnakeXSnake(SnakeXSnake.MovementDirections.Left,
				Properties.Settings.Default.Side, 9,
				this.ClientSize.Width, this.ClientSize.Height);

			// Initializing apple object.
			apple = new SnakeXApple(Properties.Settings.Default.Side,
				this.ClientSize.Width, this.ClientSize.Height);

			// Initializing score.
			score = 0;

			// Stopping menu.
			drawMenu = false;


			// Starting snake movement thread.
			movementThread = new Thread(new ThreadStart(MoveSnake));
			movementThread.Start();
		}


		#endregion


		#region Main Events Methods

		/// <summary>
		/// Form paint event.
		/// Starting device scene, drawing objects and ending scene.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void frmMain_Paint(object sender, PaintEventArgs e)
		{
			device.D3dDevice.BeginScene();
			device.D3dDevice.Clear(ClearFlags.Target, Color.Black, 0, 0);

			if (drawMenu)
			{
				// Drawing menu.
				DrawMenu();
			}
			else
			{
				// Drawing snake game.
				DrawSnake();
				DrawApple();
				DrawScore();

				CheckCollisions();
			}

			device.D3dDevice.EndScene();
			device.D3dDevice.Present();
			this.Invalidate();

			HandleKeyboardInput();
		}


		/// <summary>
		/// Direct3D device dsiposing event.
		/// Killing snake movement thread.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void D3dDevice_Disposing(object sender, EventArgs e)
		{
			if (movementThread != null)
			{
				// Aborting initialized movement thread.
				movementThread.Abort();
			}
		}


		#endregion


		#region Snake Methods

		/// <summary>
		/// Drawing the snake.
		/// </summary>
		private void DrawSnake()
		{
			square.Draw(snake.GetLink(0), snake.Side, Color.ForestGreen);
			// Drawing snake.
			for (int i = 1; i < snake.LinksCount; i++)
			{
				square.Draw(snake.GetLink(i), snake.Side, Color.LimeGreen);
			}
		}


		/// <summary>
		/// Drawing the apple.
		/// </summary>
		private void DrawApple()
		{
			square.Draw(apple.Position, apple.Side, Color.Yellow);
		}

		/// <summary>
		/// Drawing the score.
		/// </summary>
		private void DrawScore()
		{
			normalText.Draw("Score: " + score.ToString(), new Point(10, 10),
				Color.DeepSkyBlue);
		}


		/// <summary>
		/// Checking for collisions.
		/// </summary>
		private void CheckCollisions()
		{
			// Checking for apple collision.
			if (snake.AppleCollision(apple.Position))
			{
				// There was a collision. Updating score.
				score++;

				// Redrawing apple in a new position.
				apple.RandomizeNewPosition();
				bool posInUse = snake.SnakeCollision(apple.Position);
				while (posInUse)
				{
					// Position is in use by the snake. Trying new position.
					apple.RandomizeNewPosition();
					posInUse = snake.SnakeCollision(apple.Position);
				}
			}

			// Checking for self collision.
			if ((snake.SelfCollision()) || (snake.BorderCollision()))
			{
				// Snake has self or border collision. 
				// Stopping snake movement thread.
				movementThread.Abort();

				// Drawing 'Game Over' message.
				titleText.Draw("Game Over", 
					new Point(50, this.ClientSize.Height / 2 - 32), Color.Red);

				normalText.Draw("Press [Q] to quit to the menu",
					new Point(200, this.ClientSize.Height / 2 + 64), Color.Gold);
			}
		}

		/// <summary>
		/// Moving Snake.
		/// </summary>
		private void MoveSnake()
		{
			while (true)
			{
				// Moving snake by the X and Y change.
				snake.Move();

				// Sleeping until next move.
				Thread.Sleep(100);
			}
		}


		#endregion


		#region Input Methods


		private void HandleKeyboardInput()
		{
			switch (keyboard.GetPressedKey())
			{
				case Microsoft.DirectX.DirectInput.Key.LeftArrow:
					if (snake.Direction != SnakeXSnake.MovementDirections.Right)
					{
						// Changing direction.
						snake.Direction = SnakeXSnake.MovementDirections.Left;
						snake.MovementCoordinates = new Point(snake.Side, 0);
					}

					break;

				case Microsoft.DirectX.DirectInput.Key.RightArrow:
					if (snake.Direction != SnakeXSnake.MovementDirections.Left)
					{
						// Changing direction.
						snake.Direction = SnakeXSnake.MovementDirections.Right;
						snake.MovementCoordinates = new Point(-snake.Side, 0);
					}

					break;

				case Microsoft.DirectX.DirectInput.Key.UpArrow:
					if (snake.Direction != SnakeXSnake.MovementDirections.Down)
					{
						// Changing direction.
						snake.Direction = SnakeXSnake.MovementDirections.Up;
						snake.MovementCoordinates = new Point(0, snake.Side);
					}

					break;

				case Microsoft.DirectX.DirectInput.Key.DownArrow:
					if (snake.Direction != SnakeXSnake.MovementDirections.Up)
					{
						// Changing direction.
						snake.Direction = SnakeXSnake.MovementDirections.Down;
						snake.MovementCoordinates = new Point(0, -snake.Side);
					}

					break;

				case Microsoft.DirectX.DirectInput.Key.Return:
					if (drawMenu)
					{
						InitGame();
					}

					break;

				case Microsoft.DirectX.DirectInput.Key.Escape:
					if (drawMenu)
					{
						// Escape on menu - exiting from game.
						device.D3dDevice.Dispose();
						this.Close();
					}

					break;

				case Microsoft.DirectX.DirectInput.Key.Q:
					if (!drawMenu)
					{
						// Q - Quitting from game and displaying menu.
						drawMenu = true;
						movementThread.Abort();
					}

					break;
			}
		}


		#endregion


		#region Menu Methods


		private void DrawMenu()
		{
			menuSprite.Begin(SpriteFlags.None);
			menuSprite.Draw(menuTexture, new Vector3(0, 0, 0),
				new Vector3(0, 100, 0), Color.FromArgb(255, 255, 255).ToArgb());
			menuSprite.End();

			normalText.Draw("Press [Enter] to start game",
				new Point(100, 500), Color.Gold);
			normalText.Draw("Press [Escape] to quit",
				new Point(100, 524), Color.Gold);
		}


		#endregion


		#region Members


		/// <summary>
		/// The Direct3D device object.
		/// </summary>
		private SnakeXDevice device;

		/// <summary>
		/// The square object.
		/// </summary>
		private SnakeXSquare square;

		/// <summary>
		/// The normal text object.
		/// </summary>
		private SnakeXText normalText;

		/// <summary>
		/// The title text object.
		/// </summary>
		private SnakeXText titleText;

		/// <summary>
		/// The snake object.
		/// </summary>
		private SnakeXSnake snake;

		/// <summary>
		/// The apple object.
		/// </summary>
		private SnakeXApple apple;

		/// <summary>
		/// The snake movement thread object.
		/// </summary>
		private Thread movementThread;

		/// <summary>
		/// Keyboard device object.
		/// </summary>
		private SnakeXKeyboard keyboard;

		/// <summary>
		/// Game score (number of apples).
		/// </summary>
		private int score;


		/// <summary>
		/// Menu textture object.
		/// </summary>
		private Texture menuTexture;

		/// <summary>
		/// Menu sprite object.
		/// </summary>
		private Sprite menuSprite;

		/// <summary>
		/// Indicates whether to draw menu or draw game.
		/// </summary>
		private bool drawMenu;


		#endregion
	}
}
